#include <ArduinoJson.h>                  //导入JSON库，用来封装发送数据的格式
#include <DHT.h>                          //导入温湿度传感器库
#include <Servo.h>                        //控制舵机的库
#include <Arduino.h>
StaticJsonDocument<200> sendJson;          // 创建JSON对象，用来存放发送数据
StaticJsonDocument<200> readJson;          // 创建JSON对象，用来存放接收到的数据
unsigned long lastUpdateTime = 0;          //记录上次上传数据时间
const unsigned long updateInterval = 4000; // 在这里设置数据发送至云平台的时间间隔，单位为毫秒
//定义传感器相关的引脚
#define BUZZER 11
#define DHTPIN 10                         //定义温湿度传感器引脚为10
#define DHTTYPE DHT11                     //定义温湿度传感器型号为DHT11
#define CURTAIN 9                         //设定舵机（窗户）的Arduino引脚为9
#define COPEN 10                          //设定舵机的角度，窗户开启时舵机的角度
#define CCLOSE 100                        //窗户关闭时舵机的角度
#define FIRE 8                            //设定火焰传感器连接的Arduino引脚为8
#define LED 7                             //设定LED灯引脚为7
#define FAN 6                             //设定继电器（风扇）的引脚为6
/*定义烟雾传感器A0，D0引脚*/
#define Sensor_AO A0
#define Sensor_DO 3
//定义相关的变量
long ID = 100813;                // 定义设备ID号,替换成云平台生成的ID号
float temperature, humidity,smoke;    // 定义温湿度变量
int lightState,fireState,fanState,curtainState;     // 定义灯光,火焰,继电器（风扇）,窗户状态（0代表无或者关闭，1代表有或者打开）
int airTemperatureSet=26;       // 定义风扇的设定温度，默认26度

DHT dht(DHTPIN, DHTTYPE);       //创建dht对象，用于操作温湿度传感器
Servo curtainServo;                  // 创建舵机对象，用来控制窗户的舵机
// Arduino的初始化函数，仅在通电时运行一次
void setup()
{
  Serial.begin(9600);       //初始化串口，用于和esp8266进行通信，完成数据的接收与上传
  dht.begin();              //初始化温湿度传感器
  curtainServo.attach(CURTAIN);   //初始化9号引脚为舵机引脚
  pinMode(FIRE, INPUT);     //初始化火焰传感器引脚为输入模式
  pinMode(LED, OUTPUT);     //初始化LED灯引脚为输出模式
  pinMode(FAN, OUTPUT);     //初始化继电器（风扇）引脚为输出模式
}

// Arduino的loop函数，当setup函数执行完毕后进入该函数并一直循环运行
void loop()
{
  // 该函数段可完成数据定时上报的功能，并且不会阻塞loop函数的运行
  if (millis() - lastUpdateTime > updateInterval) 
  {
    sendJsonData();
    lastUpdateTime = millis();
  }
  // 当用户使用手机控制当前设备时ESP8266会通过串口向Arduino发送指令，该函数段可判断串口有没有接收到消息，并完成对用户消息的解析
  while (Serial.available() > 0) { // 判断串口缓冲区是否有消息
    String inputString = Serial.readStringUntil('\n');  //从串口缓冲区读入数据，并赋值给inputString变量（String变量为Arduino独有变量类型，可以简化字符串操作）

    //检测json数据是否完整，若通过则进行下一步的处理
    /*
      云平台向硬件下发JSON的格式为 {"Light":"1"} 类型的字符串，通常叫键值对，C语言中叫做哈希表。
      左边的"FAN"为我们在云平台中定义的标识符，右边为具体的指令
    */
    int jsonBeginAt = inputString.indexOf("{");
    int jsonEndAt = inputString.lastIndexOf("}");
    if (jsonBeginAt != -1 && jsonEndAt != -1)
    {
      inputString = inputString.substring(jsonBeginAt, jsonEndAt + 1);    //净化json数据
      deserializeJson(readJson, inputString);                             //通过ArduinoJSON库将JSON字符串转换为方便操作的对象
      // 判断接收的指令
      if (readJson.containsKey("LightState"))   //判断云平台下发的消息是否包含Light标识符，如果是则进行下一步处理
      {
        digitalWrite(LED, (int)readJson["LightState"]); // 根据Light的值来控制小灯，0为关，1为开，由于是char类型，所以我们需要转换为int
        delay(100);
        lightState = digitalRead(LED);                  // 及时更新小灯的状态信息，这样云平台才能知道是否控制成功
      }
      else if(readJson.containsKey("CurtainState"))  // 若添加了多个功能，则使用else if隔开
      {
        //更新curtain的值
        curtainState = (int)readJson["CurtainState"];
      }
      // 风扇开关设定
      else if(readJson.containsKey("FanState")){
        fanState = (int)readJson["FanState"];  // 让Arduino中的风扇设定值更新为云平台下放的值
      }
      // 风扇温度设定
      else if(readJson.containsKey("AirTemperatureSet")){
        airTemperatureSet = (int)readJson["AirTemperatureSet"];  // 让Arduino中的风扇设定值更新为云平台下放的值
      }
      delay(500);
      sendJsonData(); //向云平台发送最新的信息
    }
  }
  //判读curtainState的值是否为1，是则让舵机转到开启的角度，否则关闭
  if(curtainState){      
    curtainServo.write(COPEN); 
  } 
  else{
    curtainServo.write(CCLOSE); 
  }
  //判断风扇是否为开启的状态
  if(curtainState){      
    // 判断当前室内温度是否低于设定温度，是则触发继电器，否则关闭
    if(temperature<airTemperatureSet){
      digitalWrite(FAN,HIGH);
    }
    else{
      digitalWrite(FAN,LOW);
    }
  } 
  else{
    digitalWrite(FAN,LOW);
  }
  //读取传感器数据
  fireState = digitalRead(FIRE);  //火焰传感器
  // 温湿度传感器
  temperature = dht.readTemperature();
  humidity = dht.readHumidity();
  lightState = digitalRead(LED);  //LED灯状态
}
//上传数值
void sendJsonData()
{
  // 将数据添加到JSON对象中，左边为在云平台中定义的标识符，右边为变量
  sendJson["ID"] = ID;
  sendJson["Temperature"] = temperature;
  sendJson["Humidity"] = humidity;
  sendJson["LightState"] = lightState;
  sendJson["FanState"] = fanState;
  sendJson["CurtainState"] = curtainState;
  sendJson["FireState"] = fireState;
  sendJson["AirTemperatureSet"] = airTemperatureSet;
  sendJson["smoke"] = smoke;
  //将对象转换成字符串，并向ese8266发送消息
  serializeJson(sendJson, Serial);  
  Serial.print("\n");
}
